/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cfs.controller;

import java.sql.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.mybeans.dao.DAOException;
import org.mybeans.form.FormBeanException;

import cfs.databean.CFSTransaction;
import cfs.databean.Customer;
import cfs.databean.Fund;
import cfs.databean.FundPriceHistory;
import cfs.databean.Position;
import cfs.formbean.TransitionFundForm;
import cfs.helper.Helper;
import cfs.helper.TransactionTypes;
import cfs.model.CustomerDAO;
import cfs.model.FundDAO;
import cfs.model.FundPriceHistoryDAO;
import cfs.model.Model;
import cfs.model.PositionDAO;
import cfs.model.TransactionDAO;

/**
 * 
 * @author danny seow
 */
public class TransitionAction extends Action {
	private TransitionFundForm transFundForm;
	private TransactionDAO transactionDAO;
	private CustomerDAO customerDAO;
	private FundDAO fundDAO;
	private FundPriceHistoryDAO fundPriceHistoryDAO;
	private ArrayList<FundPriceHistory> fundPriceHistoryList;
	private ArrayList<CFSTransaction> transactionCarriedOut;
	private PositionDAO positionDAO;
	private Date date;
	EmailService mailservice;

	public TransitionAction(Model model) {
		fundPriceHistoryDAO = model.getFundHistoryDao();
		transactionDAO = model.getTransactionDao();
		customerDAO = model.getCustomerDao();
		fundDAO = model.getFundDao();
		positionDAO = model.getPositionDao();
		mailservice = new EmailService(model);
	}

	@Override
	public String getName() {
		return "transitionFund.do";
	}

	@Override
	public String perform(HttpServletRequest request) {
		List<String> errors = new ArrayList<String>();
		request.setAttribute("errors", errors);

		HashMap<Integer, String> valueList = new HashMap<Integer, String>();
		Date nextDay;
		Fund[] fundList;
		transactionCarriedOut = new ArrayList<CFSTransaction>();
		fundPriceHistoryList = new ArrayList<FundPriceHistory>();
		// date = new Date();

		try {
			fundList = fundDAO.getAll();
//			fundPriceHistoryList = fundPriceHistoryDAO.getLatestFunds();
			fundPriceHistoryList = fundPriceHistoryDAO.getLatestFundPriceList();
			date = transactionDAO.getLastTradingDay();

			if (date == null) {
				date = getPrevDay(new Date((new java.util.Date().getTime())));// set to yesterday
				nextDay = new Date((new java.util.Date().getTime())); // set to today
			} else {
				nextDay = getNextDay(date); // set to the next day
			}

			request.setAttribute("fundPriceHistory", fundPriceHistoryList);
			request.setAttribute("fundList", fundList);

			String requestedSubmit = request.getParameter("requestedSubmit");

			if (requestedSubmit == null || !requestedSubmit.equals("submitIn")) {
				valueList = generateShareValue(fundList, fundPriceHistoryList);
				request.setAttribute("valueList", valueList); // randomly
																// generated
				request.setAttribute("latestDate", formatDateTime(date));
				request.setAttribute("nextDay", formatDateTime(nextDay));
				return "TransitionPage.jsp";
			}

			// Transaction Day
			transFundForm = new TransitionFundForm(request, fundList, date);
			errors.addAll(transFundForm.getValidationErrors());

			if (errors.size() > 0) {
				valueList = transFundForm.getValueMap(); // user input
				request.setAttribute("valueList", valueList);
				request.setAttribute("latestDate", formatDateTime(date));
				request.setAttribute("nextDay", formatDateTime(nextDay));
				return "TransitionPage.jsp";
			}

			// Process the stock value changes 1st;
			date = transFundForm.getUserInputDateTypeDate();
			processEndDaySharePrice(fundList, request);

			// Process all the request
			processDummyTransaction();
			processTransitionList();
			
			// Process sent email service;
			sendEmail();

			// Process reset the available balance;
			resetAvailableBalance();
			resetAvailableShares();

			String successStatement = "Successfully Transition";
			request.setAttribute("transactionExecuted", transactionCarriedOut);
			request.setAttribute("successState", successStatement);
			return "Success.jsp";
		} catch (FormBeanException ex) {
			errors.add("Error in transition action");
			return "TransitionPage.jsp";
		} catch (DAOException exe) {
			errors.add("Error in transition action");
			return "TransitionPage.jsp";
		} catch (Exception exee) {
			errors.add("Error in transition action 3");
			return "TransitionPage.jsp";
		}
	}

	private HashMap<Integer, String> generateShareValue(Fund[] fundList,
			ArrayList<FundPriceHistory> fphList) {
		HashMap<Integer, String> valueMap = new HashMap<Integer, String>();
		double probability;
		double returnValue = 1;
		double currentValue = 0;
		for (int z = 0; z < fundList.length; z++) {
			probability = Math.random();
			for (int y = 0; y < fphList.size(); y++) {
				if (fphList.get(y).getFund_id() == fundList[z].getFund_id()) {
					currentValue = fphList.get(y).getPrice() / 100;
					if (probability > 0.9) {
						returnValue = (3 + probability) * probability;
					} else if (probability > 0.7) {
						returnValue = (2 + probability) * probability;
					} else if (probability > 0.5) {
						returnValue = (1 + probability) * probability;
					} else if (probability > 0.3) {
						returnValue = (probability - 1) * probability;
					} else if (probability > 0.1) {
						returnValue = (probability - 2) * probability;
					} else {
						returnValue = (probability - 3) * probability;
					}
				}
			}
			currentValue = currentValue + returnValue;
			double finalValue = Helper.valueRounding(currentValue, 2);
			valueMap.put(fundList[z].getFund_id(), "" + finalValue);
		}
		return valueMap;
	}

	private void processEndDaySharePrice(Fund[] fundList,
			HttpServletRequest request) throws DAOException {
		for (int z = 0; z < fundList.length; z++) {
			int fundId = fundList[z].getFund_id();
			String valueInput = request.getParameter("value" + fundId);
			double inputValue = Double.parseDouble(valueInput);
			double changingValue = Helper.valueRounding(inputValue, 2);
			long alterChangedValue = (long) (changingValue * 100);
			FundPriceHistory fph = new FundPriceHistory();
			fph.setFund_id(fundId);
			fph.setPrice(alterChangedValue);
			fph.setPrice_date(date);
			fundPriceHistoryDAO.create(fph);
		}
	}

	private String formatDateTime(Date date) {
		DateFormat dateformat = new SimpleDateFormat("yyyy/MM/dd");
		String format = dateformat.format(date);
		return format;
	}

	private void processTransitionList() throws Exception {
		List<CFSTransaction> pendingList = (List<CFSTransaction>) transactionDAO
				.getPendingTransaction("pending");
		for (int z = 0; z < pendingList.size(); z++) {
			int transitionType = pendingList.get(z).getTransaction_type();
			switch (transitionType) {
			case 1:
				processDepositCheck(pendingList.get(z));
				break;
			case 2:
				processRequestCheck(pendingList.get(z));
				break;
			case 3:
				processBuyFund(pendingList.get(z));
				break;
			case 4:
				processSellFund(pendingList.get(z));
				break;
			}
		}
	}

	private void processDummyTransaction() throws DAOException {
		CFSTransaction tran = new CFSTransaction();
		tran.setExecute_date(date);
		tran.setTransaction_type(TransactionTypes.DUMMY);
		this.transactionDAO.create(tran);
	}

	private void processDepositCheck(CFSTransaction trans) throws DAOException {
		Customer user = customerDAO.lookup(trans.getCustomer_id());
		long finalAmount = user.getCash() + trans.getAmount();

		System.out.println("");
		System.out.println("Process Deposit Check for: "
				+ user.getCustomer_id());
		System.out.println("Value to Deposit: " + trans.getAmount());
		System.out.println("Initial Value: " + user.getCash());

		// 1. Update User
		user.setCash(finalAmount);
		// 2. Update Transaction
		trans.setExecute_date(date);
		trans.setStatus("Executed");

		System.out.println("________________________________________________");
		System.out.println("Final Value: " + user.getCash());
		System.out.println("");

		try {
			// Transaction.begin();
			customerDAO.update(user);
			transactionDAO.update(trans);
			transactionCarriedOut.add(trans);
			// Transaction.commit();
		} catch (Exception e) {
			// Transaction.rollback();
		}
	}

	private void processRequestCheck(CFSTransaction trans) throws DAOException {
		long amountToWithdraw = trans.getAmount();
		Customer user = customerDAO.lookup(trans.getCustomer_id());
		long newBalance = user.getCash() - amountToWithdraw;

		System.out.println("");
		System.out.println("Process Withdraw Check for: "
				+ user.getCustomer_id());
		System.out.println("Value to Withdraw: " + trans.getAmount());
		System.out.println("Initial Value: " + user.getCash());

		// 1. Update Transaction
		trans.setExecute_date(date);
		trans.setStatus("Executed");

		// 2. Update Customer.
		user.setCash(newBalance);

		System.out.println("________________________________________________");
		System.out.println("Final Value: " + user.getCash());
		System.out.println("");

		// 3. Execute Update.
		customerDAO.update(user);
		transactionDAO.update(trans);
		transactionCarriedOut.add(trans);
	}

	private void processBuyFund(CFSTransaction trans) throws Exception {
		double amountToPurchase = trans.getAmount();
		FundPriceHistory fph = fundPriceHistoryDAO.getLatestFundByType(trans
				.getFund_id());

		System.out.println("");
		System.out.println("Process Buy Price: " + trans.getCustomer_id()
				+ " FundID: " + trans.getFund_id());
		System.out.println("Current Fund Price: " + fph.getPrice());

		double currentPrice = fph.getPrice();
		double totalShare = amountToPurchase / currentPrice;
		double totalShareAfterRound = Helper.valueRounding(totalShare, 3);
		long finalValue = (long) (totalShareAfterRound * 1000);
		// 1. Update the transaction.
		trans.setShares(finalValue);
		trans.setExecute_date(date);
		trans.setStatus("Executed");

		// 2. Update the customer.
		Customer user = customerDAO.lookup(trans.getCustomer_id());

		System.out.println("Customer Pre Invested Capital: " + user.getCash());

		long finalAmount = user.getCash() - trans.getAmount();
		user.setCash(finalAmount);

		// 3. Update the position.
		Position position;
		Position[] positionList = positionDAO.getPosition(
				trans.getCustomer_id(), trans.getFund_id());
		if (positionList != null && positionList.length > 0) {
			position = positionList[0];
			long alterValued = position.getShares() + finalValue;
			position.setShares(alterValued);
			positionDAO.update(position);
		} else if (positionList == null || positionList.length <= 0) {
			position = new Position();
			position.setCustomer_id(trans.getCustomer_id());
			position.setFund_id(trans.getFund_id());
			position.setShares(finalValue);
			positionDAO.create(position);
		}

		customerDAO.update(user);
		transactionDAO.update(trans);
		transactionCarriedOut.add(trans);

		// 4. Execute Update.

		System.out.println("User requested $ to sell: " + amountToPurchase);
		System.out.println("Shares get: " + totalShareAfterRound);
		System.out.println("________________________________________________");
		System.out.println("Final Amount of user: " + finalAmount);

	}

	private void processSellFund(CFSTransaction trans) throws Exception {
		FundPriceHistory fph = fundPriceHistoryDAO.getLatestFundByType(trans
				.getFund_id());
		Customer user = customerDAO.lookup(trans.getCustomer_id());
		double totalNumberOfSharesToSell = (double) trans.getShares() / 1000;
		double totalPriceReturned = totalNumberOfSharesToSell * fph.getPrice();

		// System.out.println("");
		// System.out.println("Process Sell Fund for: " + trans.getCustomer_id()
		// + " FundID: " + trans.getFund_id());
		// System.out.println("Current Fund Price: " + fph.getPrice());
		// System.out.println("User requested number of funds to sell: "
		// + totalNumberOfSharesToSell);

		// 1. Update the transaction
		long finalTotalPrice = (long) totalPriceReturned;

		// System.out.println("Value To Add: " + totalPriceReturned);
		// System.out.println("Initial Value: " + user.getCash());

		trans.setAmount(finalTotalPrice);
		trans.setExecute_date(date);
		trans.setStatus("Executed");

		// 2. Update the customer share position
		Position position = positionDAO.lookup(trans.getCustomer_id(),
				trans.getFund_id());
		long alteredShares = position.getShares() - trans.getShares();
		position.setShares(alteredShares);

		// System.out.println("Position Before: " + position.getShares());
		// System.out.println("Position Altered: "
		// + (totalNumberOfSharesToSell * 1000));

		// 3. Update the customer total funds
		long finalCustomerFinals = (long) (totalPriceReturned + user.getCash());
		user.setCash(finalCustomerFinals);

		// System.out.println("________________________________________________");
		// System.out.println("Final value: $" + finalCustomerFinals);
		// System.out.println("Final position: " + alteredShares);
		// System.out.println("");

		// 4. Committ to database.
		try {
			// Transaction.begin();
			customerDAO.update(user);
			positionDAO.update(position);
			transactionDAO.update(trans);
			transactionCarriedOut.add(trans);
			// Transaction.commit();
		} catch (Exception e) {
			// Transaction.rollback();
		}
	}

	private void sendEmail() {
		try {
			Customer[] cusList = customerDAO.getAll();
			for (int z = 0; z < cusList.length; z++) {
				Customer user = cusList[z];
				mailservice.activeEmailService(3, user);
			}
		} catch (Exception e) {

		}
	}

	private void resetAvailableBalance() throws DAOException {
		Customer[] customerList = customerDAO.getUsers();
		for (int z = 0; z < customerList.length; z++) {
			List<CFSTransaction> pendingTransList = transactionDAO
					.getPendingTransactionListByCustomer(
							customerList[z].getCustomer_id(),
							Helper.PENDING_STATUS);
			Customer newCustomer = customerList[z];
			long customerBalance = 0;
			customerBalance = newCustomer.getCash();
			for (int y = 0; y < pendingTransList.size(); y++) {
				if (pendingTransList.get(y).getTransaction_type() == TransactionTypes.REQUEST_CHECK) {
					customerBalance = customerBalance
							- pendingTransList.get(y).getAmount();
				}
				if (pendingTransList.get(y).getTransaction_type() == TransactionTypes.BUY_FUND) {
					customerBalance = customerBalance
							- pendingTransList.get(y).getAmount();
				}
			}
			newCustomer.setAvailablebalance(customerBalance);
			customerDAO.update(newCustomer);
		}
	}

	private void resetAvailableShares() throws DAOException {
		Customer[] customerList = customerDAO.getUsers();
		// Loop through each customer
		for (int z = 0; z < customerList.length; z++) {
			Customer newCustomer = customerList[z];
			Position[] positionList = positionDAO
					.getFundsByCustomerId(newCustomer.getCustomer_id());
			// Loop through each position
			for (int y = 0; y < positionList.length; y++) {
				Position positionCurrent = positionList[y];
				long availableShares = positionCurrent.getShares();
				List<CFSTransaction> pendingTransList = transactionDAO
						.getPendingTransactionListByCustomer(
								customerList[z].getCustomer_id(),
								Helper.PENDING_STATUS);
				for (int w = 0; w < pendingTransList.size(); w++) {
					if (pendingTransList.get(y).getTransaction_type() == TransactionTypes.SELL_FUND
							&& positionCurrent.getFund_id() == pendingTransList
									.get(y).getFund_id()) {
						availableShares = availableShares
								- pendingTransList.get(y).getShares();
					}
				}
				positionCurrent.setAvailableshares(availableShares);
				positionDAO.update(positionCurrent);
			}
		}
	}

	private Date getNextDay(Date currentDay) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(currentDay);
		cal.add(Calendar.DATE, 1);
		return new Date(cal.getTime().getTime());
	}

	private Date getPrevDay(Date currentDay) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(currentDay);
		cal.add(Calendar.DATE, -1);
		return new Date(cal.getTime().getTime());
	}
}
